package info.batcloud.fanli.core.service.impl;

import com.ctospace.archit.common.pagination.Paging;
import info.batcloud.fanli.core.dto.AdvDTO;
import info.batcloud.fanli.core.entity.Adv;
import info.batcloud.fanli.core.repository.AdvRepository;
import info.batcloud.fanli.core.repository.CrowdRepository;
import info.batcloud.fanli.core.service.AdvService;
import info.batcloud.fanli.core.service.CrowdService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.inject.Inject;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class AdvServiceImpl implements AdvService {

    @Inject
    private AdvRepository advRepository;
    @Inject
    private CrowdRepository crowdRepository;

    @Inject
    private CrowdService crowdService;

    @Override
    public void saveAdv(AdvAddParam param) {
        Adv adv = new Adv();
        adv.setCreateTime(new Date());
        adv.setDeleted(false);
        adv.setStartTime(param.getStartTime());
        adv.setEndTime(param.getEndTime());
        adv.setName(param.getName());
        adv.setPic(param.getPic());
        adv.setValid(param.isValid());
        adv.setPage(param.getPage());
        adv.setPicWidth(param.getPicWidth());
        adv.setPicHeight(param.getPicHeight());
        adv.setParams(param.getParams());
        if (param.getCrowdId() != null) {
            adv.setCrowd(crowdRepository.findOne(param.getCrowdId()));
        }
        advRepository.save(adv);
    }

    @Override
    public void updateAdv(AdvUpdateParam param) {
        Adv eAdv = advRepository.findOne(param.getId());
        BeanUtils.copyProperties(param, eAdv);
        if (param.getCrowdId() != null) {
            eAdv.setCrowd(crowdRepository.findOne(param.getCrowdId()));
        }
        advRepository.save(eAdv);
    }

    @Override
    public void validById(long id) {
        Adv eAdv = advRepository.findOne(id);
        eAdv.setValid(true);
        advRepository.save(eAdv);
    }

    @Override
    public void invalidById(long id) {
        Adv eAdv = advRepository.findOne(id);
        eAdv.setValid(false);
        advRepository.save(eAdv);
    }

    @Override
    public void deleteById(long id) {
        Adv eAdv = advRepository.findOne(id);
        eAdv.setDeleted(true);
        advRepository.save(eAdv);
    }

    @Override
    public AdvDTO findValidById(long id) {
        Date now = new Date();
        Adv adv = advRepository.findByIdAndValidAndStartTimeLessThanAndEndTimeGreaterThanAndDeleted(id, true, now, now, false);
        return toBO(adv);
    }

    @Override
    public List<AdvDTO> findByIds(List<Long> ids) {
        List<Adv> advs = advRepository.findByIdIn(ids);
        return advs.stream().map(a -> toBO(a)).collect(Collectors.toList());
    }

    @Override
    public List<AdvDTO> findValidByIds(List<Long> ids) {
        Date now = new Date();
        List<Adv> advs = advRepository.findByIdInAndValidAndStartTimeLessThanAndEndTimeGreaterThanAndDeleted(ids, true, now, now, false);
        return advs.stream().map(a -> toBO(a)).collect(Collectors.toList());
    }

    @Override
    public List<AdvDTO> filterByUserId(Long userId, List<AdvDTO> advList) {
        List<AdvDTO> list = new ArrayList<>();
        for (AdvDTO advDTO : advList) {
            if (userId == null && advDTO.getCrowdId() == null) {
                list.add(advDTO);
                continue;
            }
            if(advDTO.getCrowdId() == null) {
                list.add(advDTO);
                continue;
            }
            if (userId == null && advDTO.getCrowdId() != null) {
                continue;
            }
            if (advDTO.getCrowdId() != null
                    && !crowdService.checkUser(userId, advDTO.getCrowdId())) {
                continue;
            }
            list.add(advDTO);
        }
        return list;
    }

    @Override
    public Paging<AdvDTO> search(SearchParam param) {
        Specification<Adv> specification = (root, query, cb) -> {
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressions = predicate.getExpressions();
            if (StringUtils.isNotEmpty(param.getName())) {
                expressions.add(cb.like(root.get("name"), "%" + param.getName() + "%"));
            }
            if (param.getValid() != null) {
                expressions.add(cb.equal(root.get("valid"), param.getValid()));
            }
            expressions.add(cb.equal(root.get("deleted"), false));
            return predicate;
        };
        Sort sort = new Sort(Sort.Direction.DESC, "id");
        Pageable pageable = new PageRequest(param.getPage() - 1,
                param.getPageSize(), sort);
        Page<Adv> page = advRepository.findAll(specification, pageable);
        List<AdvDTO> dtoList = page.getContent().stream().map(a -> toBO(a)).collect(Collectors.toList());
        return Paging.of(dtoList, Long.valueOf(page.getTotalElements()).intValue(),
                param.getPage(), param.getPageSize());
    }

    private AdvDTO toBO(Adv adv) {
        AdvDTO bo = new AdvDTO();
        if (adv.getCrowd() != null) {
            bo.setCrowdId(adv.getCrowd().getId());
            bo.setCrowdName(adv.getCrowd().getName());
        }
        BeanUtils.copyProperties(adv, bo);
        return bo;
    }
}
